package org.JWCB.common.redis.service;


import com.alibaba.fastjson2.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisService {
    @Autowired
    private RedisTemplate redisTemplate;



    //************************ 操作key ***************************

    /**
     * 判断key是否存在
     * @param key key键
     * @return true 存在 false 不存在
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }


    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(final String key, final long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }


    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit
            unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key) {
        return redisTemplate.delete(key);
    }


    /**
     * 得到过期时间
     * @param key 键
     * @return 过期时间
     */
    public Long getExpire(final String key,final TimeUnit timeUnit) {
        return redisTemplate.getExpire(key,timeUnit);
    }


    //************************ 操作String类型 ***************************
    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value) {
        redisTemplate.opsForValue().set(key, value);
    }
    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Long
            timeout, final TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }


    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key, Class<T> clazz) {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        T t = operation.get(key);
        if (t instanceof String) {
            return t;
        }
        return JSON.parseObject(String.valueOf(t), clazz);
    }


    /**
     * value++
     * @param key
     * @return
     */
    public Long increment(final String key) {
        return redisTemplate.opsForValue().increment(key);
    }
                //*************** 操作list结构 ****************
    /**
     * 获取list中存储数据数量
     *
     * @param key
     * @return
     */
    public Long getListSize(final String key) {
        return redisTemplate.opsForList().size(key);
    }


    /**
     * 从redis中批量获取
     */
    public <T> List<T> multiGet(final List<String> keyList,Class<T> clazz) {
        List list = redisTemplate.opsForValue().multiGet(keyList);
        if(list == null || list.size() == 0) {
            return null;
        }
        List<T> res = new ArrayList<>();
        for(Object o : list) {
            res.add(JSON.parseObject(String.valueOf(o),clazz));
        }
        return res;
    }

    /**
     * 往redis中批量插入数据
     */
    public <K,V> void multiSet(Map<? extends K,? extends  V> map) {
        redisTemplate.opsForValue().multiSet(map);
    }

    /**
     * 获取list中指定范围数据
     *
     * @param key
     * @param start
     * @param end
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> List<T> getCacheListByRange(final String key, long start, long
            end, Class<T> clazz) {
        List range = redisTemplate.opsForList().range(key, start, end);
        if (CollectionUtils.isEmpty(range)) {
            return null;
        }
        return JSON.parseArray(JSON.toJSONString(range), clazz);
    }

    /**
     * 底层使用list结构存储数据(尾插 批量插入)
     */
    public <T> Long rightPushAll(final String key, Collection<T> list) {
        return redisTemplate.opsForList().rightPushAll(key, list);
    }

    /**
     * 底层使用list结构存储数据(头插)
     */
    public <T> Long leftPushForList(final String key, T value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }
    /**
     * 底层使用list结构,删除指定数据
     */
    public <T> Long removeForList(final String key, T value) {
        return redisTemplate.opsForList().remove(key, 1L, value);
    }


    /**
     * 获取在列表中的位置
     */
    public <T> Long indexOfForList(final String key,T value)
    {
        return redisTemplate.opsForList().indexOf(key,value);
    }


    public <T> T indexForList(final String key,long index,Class<T> clazz) {
        Object value = redisTemplate.opsForList().index(key,index);
        return JSON.parseObject(String.valueOf(value),clazz);
    }


    //************************ 操作Hash类型 ***************************
    public <T> T getCacheMapValue(final String key, final String hKey,
                                  Class<T> clazz) {
        Object cacheMapValue = redisTemplate.opsForHash().get(key, hKey);
        if (cacheMapValue != null) {
            return JSON.parseObject(String.valueOf(cacheMapValue), clazz);
        }
        return null;
    }
    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @param clazz 待转换对象类型
     * @param <T> 泛型
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final
    Collection<String> hKeys, Class<T> clazz) {
        List list = redisTemplate.opsForHash().multiGet(key, hKeys);
        List<T> result = new ArrayList<>();
        for (Object item : list) {
            result.add(JSON.parseObject(JSON.toJSONString(item), clazz));
        }
        return result;
    }



    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final
    T value) {
        redisTemplate.opsForHash().put(key, hKey, value);
    }
    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <K, T> void setCacheMap(final String key, final Map<K, T> dataMap) {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }
    public Long deleteCacheMapValue(final String key, final String hKey) {
        return redisTemplate.opsForHash().delete(key, hKey);
    }


    /**
     * hash += n
     */
    public Long incrementHashValue(String key, String hKey, long num) {
        return redisTemplate.opsForHash().increment(key,hKey,num);
    }



    //************************ 操作zset ***************************

    /**
     * 添加元素到有序集合
     * @param key 键
     * @param value 值
     * @param score 分数
     * @return 是否添加成功
     */
    public <T> Boolean zAdd(final String key, final T value, final double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 按分数范围获取元素（正序）
     * @param key 键
     * @param min 最小分数
     * @param max 最大分数
     * @param clazz 返回类型
     * @return 元素列表
     */
    public <T> List<T> zRangeByScore(final String key, final double min, final double max, Class<T> clazz) {
        return zRangeByScore(key, min, max, 0L, -1L, clazz);
    }

    /**
     * 按分数范围获取元素（正序）带分页
     * @param key 键
     * @param min 最小分数
     * @param max 最大分数
     * @param offset 偏移量
     * @param count 数量
     * @param clazz 返回类型
     * @return 元素列表
     */
    public <T> List<T> zRangeByScore(final String key, final double min, final double max,
                                     final long offset, final long count, Class<T> clazz) {
        Set<Object> values = redisTemplate.opsForZSet().rangeByScore(key, min, max, offset, count);
        return parseObjectSet(values, clazz);
    }

    /**
     * 按分数范围获取元素（倒序）
     * @param key 键
     * @param min 最小分数
     * @param max 最大分数
     * @param clazz 返回类型
     * @return 元素列表
     */
    public <T> List<T> zRevRangeByScore(final String key, final double min, final double max, Class<T> clazz) {
        return zRevRangeByScore(key, min, max, 0L, -1L, clazz);
    }

    /**
     * 按分数范围获取元素（倒序）带分页
     * @param key 键
     * @param min 最小分数
     * @param max 最大分数
     * @param offset 偏移量
     * @param count 数量
     * @param clazz 返回类型
     * @return 元素列表
     */
    public <T> List<T> zRevRangeByScore(final String key, final double min, final double max,
                                        final long offset, final long count, Class<T> clazz) {
        Set<Object> values = redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, offset, count);
        return parseObjectSet(values, clazz);
    }

    /**
     * 按索引范围获取元素（正序）
     * @param key 键
     * @param start 开始索引
     * @param end 结束索引
     * @param clazz 返回类型
     * @return 元素列表
     */
    public <T> List<T> zRange(final String key, final long start, final long end, Class<T> clazz) {
        Set<Object> values = redisTemplate.opsForZSet().range(key, start, end);
        return parseObjectSet(values, clazz);
    }

    /**
     * 按索引范围获取元素（倒序）
     * @param key 键
     * @param start 开始索引
     * @param end 结束索引
     * @param clazz 返回类型
     * @return 元素列表
     */
    public <T> List<T> zRevRange(final String key, final long start, final long end, Class<T> clazz) {
        Set<Object> values = redisTemplate.opsForZSet().reverseRange(key, start, end);
        return parseObjectSet(values, clazz);
    }

    /**
     * 获取元素分数
     * @param key 键
     * @param value 值
     * @return 分数
     */
    public Double zScore(final String key, final Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 统计分数范围内元素数量
     * @param key 键
     * @param min 最小分数
     * @param max 最大分数
     * @return 数量
     */
    public Long zCount(final String key, final double min, final double max) {
        return redisTemplate.opsForZSet().count(key, min, max);
    }

    /**
     * 获取有序集合大小
     * @param key 键
     * @return 元素总数
     */
    public Long zCard(final String key) {
        return redisTemplate.opsForZSet().size(key);
    }

    /**
     * 增加元素分数
     * @param key 键
     * @param value 值
     * @param delta 增量
     * @return 新分数
     */
    public Double zIncrementScore(final String key, final Object value, final double delta) {
        return redisTemplate.opsForZSet().incrementScore(key, value, delta);
    }

    /**
     * 获取元素排名（正序）
     * @param key 键
     * @param value 值
     * @return 排名（从0开始）
     */
    public Long zRank(final String key, final Object value) {
        return redisTemplate.opsForZSet().rank(key, value);
    }

    /**
     * 获取元素排名（倒序）
     * @param key 键
     * @param value 值
     * @return 排名（从0开始）
     */
    public Long zRevRank(final String key, final Object value) {
        return redisTemplate.opsForZSet().reverseRank(key, value);
    }

    /**
     * 删除元素
     * @param key 键
     * @param values 要删除的值
     * @return 删除数量
     */
    public Long zRemove(final String key, final Object... values) {
        return redisTemplate.opsForZSet().remove(key, values);
    }

    /**
     * 根据排名范围删除元素
     * @param key 键
     * @param start 开始排名
     * @param end 结束排名
     * @return 删除数量
     */
    public Long zRemoveRange(final String key, final long start, final long end) {
        return redisTemplate.opsForZSet().removeRange(key, start, end);
    }

    /**
     * 根据分数范围删除元素
     * @param key 键
     * @param min 最小分数
     * @param max 最大分数
     * @return 删除数量
     */
    public Long zRemoveRangeByScore(final String key, final double min, final double max) {
        return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
    }

    /**
     * 对象转换方法
     */
    private <T> List<T> parseObjectSet(Set<Object> values, Class<T> clazz) {
        if (CollectionUtils.isEmpty(values)) {
            return Collections.emptyList();
        }
        List<T> result = new ArrayList<>(values.size());
        for (Object value : values) {
            if (value instanceof String) {
                result.add(JSON.parseObject((String) value, clazz));
            } else {
                result.add(JSON.parseObject(JSON.toJSONString(value), clazz));
            }
        }
        return result;
    }


}
